// Copyright 2014 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "src/arguments-inl.h"
#include "src/base/bits.h"
#include "src/bootstrapper.h"
#include "src/counters.h"
#include "src/heap/heap-inl.h" // For ToBoolean. TODO(jkummerow): Drop.
#include "src/isolate-inl.h"
#include "src/runtime/runtime-utils.h"

namespace v8 {
namespace internal {

    RUNTIME_FUNCTION(Runtime_IsValidSmi)
    {
        SealHandleScope shs(isolate);
        DCHECK_EQ(1, args.length());

        CONVERT_NUMBER_CHECKED(int32_t, number, Int32, args[0]);
        return isolate->heap()->ToBoolean(Smi::IsValid(number));
    }

    RUNTIME_FUNCTION(Runtime_StringToNumber)
    {
        HandleScope handle_scope(isolate);
        DCHECK_EQ(1, args.length());
        CONVERT_ARG_HANDLE_CHECKED(String, subject, 0);
        return *String::ToNumber(isolate, subject);
    }

    // ES6 18.2.5 parseInt(string, radix) slow path
    RUNTIME_FUNCTION(Runtime_StringParseInt)
    {
        HandleScope handle_scope(isolate);
        DCHECK_EQ(2, args.length());
        CONVERT_ARG_HANDLE_CHECKED(Object, string, 0);
        CONVERT_ARG_HANDLE_CHECKED(Object, radix, 1);

        // Convert {string} to a String first, and flatten it.
        Handle<String> subject;
        ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, subject,
            Object::ToString(isolate, string));
        subject = String::Flatten(isolate, subject);

        // Convert {radix} to Int32.
        if (!radix->IsNumber()) {
            ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, radix,
                Object::ToNumber(isolate, radix));
        }
        int radix32 = DoubleToInt32(radix->Number());
        if (radix32 != 0 && (radix32 < 2 || radix32 > 36)) {
            return ReadOnlyRoots(isolate).nan_value();
        }

        double result = StringToInt(isolate, subject, radix32);
        return *isolate->factory()->NewNumber(result);
    }

    // ES6 18.2.4 parseFloat(string)
    RUNTIME_FUNCTION(Runtime_StringParseFloat)
    {
        HandleScope shs(isolate);
        DCHECK_EQ(1, args.length());
        CONVERT_ARG_HANDLE_CHECKED(String, subject, 0);

        double value = StringToDouble(isolate, subject, ALLOW_TRAILING_JUNK,
            std::numeric_limits<double>::quiet_NaN());

        return *isolate->factory()->NewNumber(value);
    }

    RUNTIME_FUNCTION(Runtime_NumberToString)
    {
        HandleScope scope(isolate);
        DCHECK_EQ(1, args.length());
        CONVERT_NUMBER_ARG_HANDLE_CHECKED(number, 0);

        return *isolate->factory()->NumberToString(number);
    }

    // Compare two Smis x, y as if they were converted to strings and then
    // compared lexicographically. Returns:
    // -1 if x < y
    //  0 if x == y
    //  1 if x > y
    // TODO(szuend): Remove once the call-site in src/js/array.js is gone.
    RUNTIME_FUNCTION(Runtime_SmiLexicographicCompare)
    {
        SealHandleScope shs(isolate);
        DCHECK_EQ(2, args.length());
        CONVERT_ARG_CHECKED(Smi, x_value, 0);
        CONVERT_ARG_CHECKED(Smi, y_value, 1);

        return Object(Smi::LexicographicCompare(isolate, x_value, y_value));
    }

    RUNTIME_FUNCTION(Runtime_MaxSmi)
    {
        SealHandleScope shs(isolate);
        DCHECK_EQ(0, args.length());
        return Smi::FromInt(Smi::kMaxValue);
    }

    RUNTIME_FUNCTION(Runtime_IsSmi)
    {
        SealHandleScope shs(isolate);
        DCHECK_EQ(1, args.length());
        CONVERT_ARG_CHECKED(Object, obj, 0);
        return isolate->heap()->ToBoolean(obj->IsSmi());
    }

    RUNTIME_FUNCTION(Runtime_GetHoleNaNUpper)
    {
        HandleScope scope(isolate);
        DCHECK_EQ(0, args.length());
        return *isolate->factory()->NewNumberFromUint(kHoleNanUpper32);
    }

    RUNTIME_FUNCTION(Runtime_GetHoleNaNLower)
    {
        HandleScope scope(isolate);
        DCHECK_EQ(0, args.length());
        return *isolate->factory()->NewNumberFromUint(kHoleNanLower32);
    }

} // namespace internal
} // namespace v8
